using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using ScrewTurn.Wiki;
using ScrewTurn.Wiki.PluginFramework;

namespace ScrewTurnWiki.Plugins.MahrEdv
{
    /// <summary>
    /// Usage: {cattop}
    /// 
    /// This plugin creates a simple file based access counter for all pages.
    /// When a page is accessed, it increments the counter for that page.
    /// All pages that contain the cattop command take part in this.
    ///
    /// The second part of the command is, that it creates a linked list to the 10
    /// most often used pages in the same categories, as the currently showing page.
    /// 
    /// To put it all in a simple nutshell, it creates a linked list of the 10 most often viewed
    /// pages in the category of the current page. 
    /// </summary>
    public class CatTop : IFormatterProviderV30
    {
        private static readonly ComponentInformation Info = new ComponentInformation("CatTop", "Mahr EDV GmbH",
                                                                                     "1.0.0.1",
                                                                                     "http://www.mahr-edv.de",
                                                                                     "http://www.mahr-edv.de");

        private static readonly Regex CustomTagRegex = new Regex(@"{cattop}",
                                                                 RegexOptions.Compiled | RegexOptions.Singleline |
                                                                 RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);

        public void Init(IHostV30 host, string config)
        {
        }

        public void Shutdown()
        {
        }

        public string Format(string raw, ContextInformation context, FormattingPhase phase)
        {
            try
            {
                var buffer = new StringBuilder(raw);
                var block = FindAndRemoveFirstOccurrence(buffer);
                var text = "";

                /* In case we have a cattop somewhere in there, lets do some work. */
                if (block.Key != -1)
                {
                    IncreaseStatistics(context);

                    text = GenerateContent(context);
                }

                /* Replace all remaining occurrences of the statement */
                if (block.Key != -1)
                {
                    do
                    {
                        buffer.Insert(block.Key, text);
                        block = FindAndRemoveFirstOccurrence(buffer);
                    } while (block.Key != -1);
                }

                return buffer.ToString();

            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

        private string GenerateContent(ContextInformation context)
        {
            /* Collect statistics of the pages in the context */
            var statistics = GetTop10Pages(context);
            
            /* Get namespace and categories */
            var currentNamespace = Tools.DetectCurrentNamespaceInfo();

            var sb = new StringBuilder();
            sb.Append("<ul>");

            for (int i = 0; i < Math.Min(statistics.Count, 10); i++)
            {
                var content = Content.GetPageContent(statistics[i].PageInfo, true);
                if ( content != null )
                {
                    sb.Append(@"<li><a href=""");
                    UrlTools.BuildUrl(sb, new[] { statistics[i].PageInfo.FullName + ".ashx" });
                    sb.Append(@""">");
                    sb.Append(content.Title);
                    sb.Append(" (" + statistics[i].Count + " Hits)");
                    sb.Append("</a></li>");
                }
            }
            sb.Append("</ul>");

            return  sb.ToString();
        }

        private List<PageInfoWithCount> GetTop10Pages(ContextInformation context)
        {
            var result = new List<PageInfoWithCount>();

            var page = Pages.FindPage(context.Page.FullName);
            if ( page == null ) return result;

            var categories = Pages.GetCategoriesForPage(page);
            var preresult = new Dictionary<PageInfo, int>();

            for ( int i = 0; i < categories.Length; i++ )
            {
                // reload so that all registered pages show up (won't happen when we just access categories[i].Pages)
                var category = Pages.FindCategory(categories[i].FullName);
                var subpages = category.Pages;
                for ( int j = 0; j < subpages.Length; j++ )
                {
                    var subpage = Pages.FindPage(subpages[j]);

                    if (!preresult.ContainsKey(subpage))
                        preresult.Add(subpage, GetCounterOf(subpage));
                }
            }

            for ( int i = 0; i < 10; i++ )
            {
                var temp = GetTopPage(preresult);
                if ( temp != null )
                {
                    result.Add(temp);
                    preresult.Remove(temp.PageInfo);
                }
            }

            return result;
        }

        private PageInfoWithCount GetTopPage(Dictionary<PageInfo, int> preresult)
        {
            PageInfoWithCount top = null;

            foreach (var entry in preresult)
                if (top == null || entry.Value > top.Count)
                {
                    top = new PageInfoWithCount(entry.Key, entry.Value);
                }

            return top;
        }

        private static int GetCounterOf(PageInfo page)
        {
            var counterText = Settings.Provider.GetSetting("pagecounter." + page.FullName) ?? "0";
            var counter = 0;
            try
            {
                counter = Convert.ToInt32(counterText);
            }
            catch
            {
            }
            return counter;
        }


        /// <summary>
        /// Increase the page counter in case we should.
        /// </summary>
        /// <param name="context"></param>
        private static void IncreaseStatistics(ContextInformation context)
        {
            if (context.ForIndexing) return;
            if (context.ForWysiwyg) return;

            var counterText = Settings.Provider.GetSetting("pagecounter." + context.Page.FullName) ?? "0";
            var counter = 0;
            try
            {
                counter = Convert.ToInt32(counterText);
            }
            catch 
            {
            }

            Settings.Provider.SetSetting("pagecounter." + context.Page.FullName, (counter + 1).ToString());
        }

        public ComponentInformation Information
        {
            get { return Info; }
        }

        public string ConfigHelpHtml
        {
            get { return null; }
        }

        private static KeyValuePair<int, string> FindAndRemoveFirstOccurrence(StringBuilder buffer)
        {
            var match = CustomTagRegex.Match(buffer.ToString());

            if (match.Success)
            {
                buffer.Remove(match.Index, match.Length);

                return new KeyValuePair<int, string>(match.Index, match.Value);
            }

            return new KeyValuePair<int, string>(-1, null);
        }

        public bool PerformPhase1
        {
            get { return false; }
        }

        public bool PerformPhase2
        {
            get { return true; }
        }

        public bool PerformPhase3
        {
            get { return false; }
        }

        public int ExecutionPriority
        {
            get { return 50; }
        }

        public string PrepareTitle(string title, ContextInformation context)
        {
            return title;
        }
    }

    internal class PageInfoWithCount
    {
        public PageInfo PageInfo { get; protected set; }
        public int Count { get; protected set; }

        public PageInfoWithCount(PageInfo pageInfo, int count)
        {
            PageInfo = pageInfo;
            Count = count;
        }
    }
}